/*
	Floculate - An arcadey action physics construction game.
	Copyright (C) 2008  Bill Whitacre

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#if defined(__WIN32__)
#	include <windows.h>
#	include <stdlib.h>
#	include <stdio.h>
#	include <ctype.h>
#else
#	include <stdlib.h>
#	include <stdio.h>
#	include <ctype.h>
#	include <GL/glx.h>
#endif
#include <GL/gl.h>

#include "math.h"
#include "system.h"
#include "font.h"
#include "draw.h"
#include "primitives.h"
#include "ortho.h"
#include "projection.h"
#include "shader.h"

#ifndef MATH_PI
#	ifndef M_PI
#		define MATH_PI 3.14159f
#	else
#		define MATH_PI M_PI
#	endif
#endif

//#define SPHERE_SOLID_BASE	rat_octahedron_solid_base
#define SPHERE_SOLID_BASE	rat_icosahedron_solid_base
#define VID_WIDTH			800
#define VID_HEIGHT			600
#define VID_ASPECT_RATIO	((double)VID_WIDTH/(double)VID_HEIGHT)

rat_glyph_font *glyphfont;
rat_texture_font *bigfont;
rat_texture_font *outfont;

rat_frustum pfrustum;
rat_projection projection;

RatMathEuler modrot,*tempeul;
RatMathQuaternion *tempquat;
RatMathQuaternion currot;
RatMathQuaternion lagrot;
RatMathMatrix4 *rotmat;
float tempmatrix[16];
float mousex,mousey;

int detail_level=0,updown=0,downdown=0,leftdown=0,rightdown=0,wdown=0,wireframemenu=0;
unsigned int sphere_list,cylinder_list;
unsigned int vidwidth=VID_WIDTH,vidheight=VID_HEIGHT;

void adjust_detail(int new_level);
void enable_light();

rat_shader *shader;

int shininess=100;

int main(int argc,char *argv[])
{
	int running;
	float cntxv;
	char c;
	
	if (argc>1)
	{
		if (!strcmp(argv[1],"-f"))
		{
ask_fs_again:
			printf("Would you like to run in full screen? (Y/n) ");
			c=tolower(getchar());

			switch (c)
			{
			case 'y': case 'n': case '\n':
				break;
			default:
				printf("No, stupid.  Enter a Y or an N (not case sensitive).\n");
				getchar();
				goto ask_fs_again;
				break;
			};
		}
		else
			c='n';
	}
	
	if (!rat_start_system_core("Floculate",VID_WIDTH,VID_HEIGHT,16,c=='y'?1:0)) return 1;
	if (!rat_start_font_system()) return 1;
	
	cylinder_list=glGenLists(1);
	glNewList(cylinder_list,GL_COMPILE);
		rat_draw_cylinder(8*(detail_level+1),4*(detail_level+1),2*(detail_level+1),0.75f,3.25f);
	glEndList();
	
	sphere_list=glGenLists(1);
	glNewList(sphere_list,GL_COMPILE);
		rat_draw_sphere(detail_level,1.5f,SPHERE_SOLID_BASE);
	glEndList();
	
	glClearColor(0.125f,0.125f,0.25f,1.0f);
	glEnable(GL_CULL_FACE);
	glEnable(GL_BLEND);
	glEnable(GL_LIGHTING);
	glDisable(GL_DITHER);
	
	glPolygonMode(GL_FRONT,GL_FILL);
	glCullFace(GL_BACK);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	
	glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
	glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
	
	glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER,1);
	
	rat_ortho_set_mode(rat_ortho_res_vres);
	rat_ortho_set_res(VID_WIDTH,VID_HEIGHT);
	rat_ortho_set_virtual_res(VID_WIDTH,VID_HEIGHT);
	
	glyphfont=rat_glyph_font_load("font.ttf",128);
	bigfont=rat_texture_font_from_glyph_font(glyphfont);
	rat_glyph_font_destroy(glyphfont);

	glyphfont=rat_glyph_font_load("small.ttf",14);
	outfont=rat_texture_font_from_glyph_font(glyphfont);
	rat_glyph_font_destroy(glyphfont);
	
	projection.vpx=0;
	projection.vpy=0;
	projection.vpw=VID_WIDTH;
	projection.vph=VID_HEIGHT;
	projection.aspect=VID_ASPECT_RATIO;
	projection.yfov=MATH_PI/4.0f;
	projection.znear=1.0f;
	projection.zfar=750.0f;
	
	rat_build_frustum(&projection,&pfrustum);
	
	RatMathQuaternionWriteIdentity(&currot);
	RatMathQuaternionWriteIdentity(&lagrot);
	
	shader=rat_load_shader("shaders","simple_lighting",stdout);
	if (!shader)
	{
		printf("failed to load GPU program(s)!\n");
		return 1;
	}
	
	running=1;
    while (running)
    {
        rat_handle_events();
		if (rat_appterm_event()||rat_is_key_down(RAT_KEY_ESCAPE)) running=0;
		if (rat_resized_event())
		{
			rat_video_size(&vidwidth,&vidheight);
			rat_ortho_set_res(vidwidth,vidheight);
			
			projection.vpx=0;
			projection.vpy=0;
			projection.vpw=vidwidth;
			projection.vph=vidheight;
			projection.aspect=(double)vidwidth/(double)vidheight;
			rat_build_frustum(&projection,&pfrustum);
		}
		
		if (rat_is_key_down(RAT_KEY_UP))
			updown=1;
		else if (updown)
		{
			updown=0;
			detail_level=(++detail_level)>10?10:detail_level;
			adjust_detail(detail_level);
		}
		
		if (rat_is_key_down(RAT_KEY_DOWN))
			downdown=1;
		else if (downdown)
		{
			downdown=0;
			detail_level=(--detail_level)<0?0:detail_level;
			adjust_detail(detail_level);
		}
		
		if (rat_is_key_down(RAT_KEY_LEFT))
		{
			leftdown=1;
			shininess=(++shininess)>128?128:shininess;
		}
		else if (leftdown)
			leftdown=0;
		
		if (rat_is_key_down(RAT_KEY_RIGHT))
		{
			rightdown=1;
			shininess=(--shininess)<1?1:shininess;
		}
		else if (downdown)
			rightdown=0;
		
		if (rat_is_key_down(RAT_KEY_KEY_Q))
			wdown=1;
		else if (wdown)
		{
			wdown=0;
			wireframemenu=!wireframemenu;
		}
		
		memset(&modrot,0,sizeof(RatMathEuler));
		
		if (rat_is_key_down(RAT_KEY_KEY_A)) modrot.y+=1;
		if (rat_is_key_down(RAT_KEY_KEY_D)) modrot.y-=1;
		if (rat_is_key_down(RAT_KEY_KEY_W)) modrot.z+=1;
		if (rat_is_key_down(RAT_KEY_KEY_S)) modrot.z-=1;
		
		RatMathVector3Mult(&modrot,0.01f);
		
		tempquat=RatMathQuaternionCreateFromEuler(&modrot);
		RatMathQuaternionMult(&currot,tempquat);
		free((void *)tempquat);
		
		tempquat=RatMathQuaternionSlerp(&lagrot,&currot,0.08f);
		memcpy(&lagrot,tempquat,sizeof(RatMathQuaternion));
		free((void *)tempquat);
		
		RatMathQuaternionNormalize(&lagrot);
		RatMathQuaternionNormalize(&currot);
        
        rat_apply_shader(shader);
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
        rat_projection_push_frustum(&pfrustum);
		{
        	glPushAttrib(GL_CURRENT_BIT|GL_POLYGON_BIT|GL_LIGHTING_BIT|GL_ENABLE_BIT|GL_TRANSFORM_BIT);
		    	glMatrixMode(GL_MODELVIEW);
		    	
		    	enable_light();
				if (wireframemenu)
				{
					glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);
					glDisable(GL_CULL_FACE);
				}
				else
				{
					glPolygonMode(GL_FRONT,GL_FILL);
					glEnable(GL_CULL_FACE);
				}
				
		    	glPushMatrix();
					glLoadIdentity();
					glTranslatef(0,0,-5.0f);
					
					rotmat=RatMathMatrix4CreateFromQuaternion(&lagrot);
					RatMathMatrix4GetTransposeArray(rotmat,tempmatrix);
					glMultMatrixf(tempmatrix);
					free((void *)rotmat);
					
					// draw cylinder
					glPushMatrix();
						glTranslatef(2.0f,0,0);
						glCallList(cylinder_list);
					glPopMatrix();
					
					// draw sphere
					glPushMatrix();
						glTranslatef(-2.0f,0,0);
						glCallList(sphere_list);
					glPopMatrix();
				glPopMatrix();
			glPopAttrib();
		}
		rat_projection_pop();
		rat_apply_shader(NULL);
		rat_ortho_push();
		{
			// dump rotation info
			char buf[2048];
			sprintf(buf,"lagrot xi,yj,zk,w1 = {%3.3f,%3.3f,%3.3f,%3.3f}",lagrot.x,lagrot.y,lagrot.z,lagrot.w);
			rat_texture_font_render_text(outfont,5,5,buf);
			tempeul=RatMathEulerCreateFromQuaternion(&lagrot);
			sprintf(buf,"lagrot pitch,yaw,roll = {%3.3f,%3.3f,%3.3f}",tempeul->x,tempeul->y,tempeul->z);
			rat_texture_font_render_text(outfont,5,5+(rat_texture_font_height(outfont)+8),buf);
			free((void *)tempeul);
			sprintf(buf,"shininess = %i",shininess);
			rat_texture_font_render_text(outfont,5,5+(rat_texture_font_height(outfont)+8)*2,buf);
		}
		rat_ortho_pop();
		rat_update_video();
	}
	
	rat_apply_shader(NULL);
	rat_destroy_shader(shader);
	
	glDeleteLists(sphere_list,1);
	glDeleteLists(cylinder_list,1);
	
	rat_texture_font_destroy(bigfont);
	rat_texture_font_destroy(outfont);
	rat_stop_font_system();
	rat_stop_system_core();
	return 0;
}

void enable_light()
{
	float material_ks[]={0.75f,0.75f,0.75f,1.0f};
	
	float light0_ka[]={0.1f,0.1f,0.1f,1.0f};
	float light0_kd[]={0.5f,0.5f,0.5f,1.0f};
	float light0_metric[4]={1,1,1,0};
	
	float light1_ka[]={0.0f,0.0f,0.0f,1.0f};
	float light1_kd[]={0.4f,0.7f,0.7f,1.0f};
	float light1_metric[4]={-12,-12,0,1};
	
	float light2_ka[]={0.0f,0.0f,0.0f,1.0f};
	float light2_kd[]={0.7f,0.2f,0.2f,1.0f};
	float light2_metric[4]={0,2,-3,1};
		    	
	glLightfv(GL_LIGHT0,GL_AMBIENT,light0_ka);
	glLightfv(GL_LIGHT0,GL_DIFFUSE,light0_kd);
	glLightfv(GL_LIGHT0,GL_SPECULAR,light0_kd);
	glLightfv(GL_LIGHT0,GL_POSITION,light0_metric);
	glEnable(GL_LIGHT0);
	
	glLightfv(GL_LIGHT1,GL_AMBIENT,light1_ka);
	glLightfv(GL_LIGHT1,GL_DIFFUSE,light1_kd);
	glLightfv(GL_LIGHT1,GL_SPECULAR,light1_kd);
	glLightfv(GL_LIGHT1,GL_POSITION,light1_metric);
	glLightf(GL_LIGHT1,GL_LINEAR_ATTENUATION,0.2f);
	glEnable(GL_LIGHT1);
	
	glLightfv(GL_LIGHT2,GL_AMBIENT,light2_ka);
	glLightfv(GL_LIGHT2,GL_DIFFUSE,light2_kd);
	glLightfv(GL_LIGHT2,GL_SPECULAR,light2_kd);
	glLightfv(GL_LIGHT2,GL_POSITION,light2_metric);
	glLightf(GL_LIGHT2,GL_LINEAR_ATTENUATION,0.2f);
	glEnable(GL_LIGHT2);
	
	glMaterialf(GL_FRONT,GL_SHININESS,(float)shininess);
	glMaterialfv(GL_FRONT,GL_SPECULAR,material_ks);
}

void adjust_detail(int new_level)
{
	glDeleteLists(cylinder_list,1);
	glDeleteLists(sphere_list,1);
	cylinder_list=glGenLists(1);
	glNewList(cylinder_list,GL_COMPILE);
		rat_draw_cylinder(8*(new_level+1),4*(new_level+1),2*(new_level+1),0.75f,3.25f);
	glEndList();
	sphere_list=glGenLists(1);
	glNewList(sphere_list,GL_COMPILE);
		rat_draw_sphere(new_level,1.5f,SPHERE_SOLID_BASE);
	glEndList();
}

